<template>
  <el-select
    @visible-change="handleFetch"
    v-bind="$attrs"
    v-model="state.value"
    :loading="loading"
    @change="handleChange"
    filterable
    clearable
    :disabled="props.disabled"
  >
    <el-option
      v-for="item in selectOptions"
      :key="item[props.valueField]"
      :label="item[props.labelField]"
      :value="item[props.valueField]"
    />
  </el-select>
</template>

<script setup>
import { isObject } from '@/utils/tools'

let lastParams = null
const attrs = useAttrs()
const emit = defineEmits(['update:modelValue', 'change'])
const props = defineProps({
  api: {
    type: Function,
    default: null
  },
  // api params
  params: {
    type: [String, Array, Number, Object],
    require: false
  },
  resultField: {
    type: String,
    default: 'data'
  },
  labelField: {
    type: String,
    default: 'label'
  },
  valueField: {
    type: String,
    default: 'value'
  },
  // 是否立即触发fetch
  immediate: {
    type: Boolean,
    default: false
  },
  // 是否每次都触发fetch
  alwaysLoad: {
    type: Boolean,
    default: false
  },
  // 请求前，是否校验请求参数是否存在
  checkParams: {
    type: Boolean,
    default: true
  },
  //是否自动选择第一个
  autoFirst: {
    type: Boolean,
    default: false
  },
  // 主动传入选项，则api相关无效
  options: {
    type: Array,
    default: null
  },
  disabled: {
    type: Boolean,
    default: false
  }
})

const selectOptions = ref([])
const loading = ref(false)
const isFirstLoad = ref(true)
const state = reactive({ value: '' })

onMounted(() => {
  props.immediate && fetch()

  // 监听传入的参数，参数变化时，触发联动效果
  watch(
    () => props.params,
    (val, oldVal) => {
      // 参数是否变化判断
      if (JSON.stringify(val) === JSON.stringify(oldVal)) return
      if (selectOptions.value.length && JSON.stringify(val) === JSON.stringify(lastParams)) return

      props.immediate && fetch()

      // 参数变化，则重置 -- 前置条件：1、非第一次加载  2、传入前的参数不为空
      if (isFirstLoad.value) return
      if (isObject(oldVal) && Object.values(oldVal)?.some((v) => v === null || v === undefined))
        return

      state.value = ''
      selectOptions.value = []
      isFirstLoad.value = true
      handleChange('')
    },
    { deep: true }
  )
})

// 当传入options时，给下拉选项赋值
watch(
  () => props.options,
  (val) => {
    val?.length && fetch()
  },
  { deep: true, immediate: true }
)

// 获取下拉列表数据
async function fetch() {
  // 当传入的是选项时
  if (props.options?.length) {
    selectOptions.value = props.options ?? []
  } else {
    //当传入的是请求方法
    const { params, checkParams, resultField, api } = props
    lastParams = params

    // 判断请求方法及是否需要验证参数
    if (typeof api !== 'function') return
    if (checkParams && !params) return
    selectOptions.value = []
    loading.value = true
    try {
      const res = await api(params)
      // console.log('api', res)
      if (res) selectOptions.value = res[resultField] ?? []
    } finally {
      loading.value = false
    }
  }

  if (selectOptions.value.length) {
    if (props.autoFirst) {
      state.value = selectOptions.value[0]?.[props.valueField] ?? ''
    }
  }

  // -- 新增判断：当初始绑定的modelValue值不为空并且下拉选项和值不匹配，则清空当前值
  if (attrs?.modelValue && isFirstLoad.value) {
    let needClear = true

    selectOptions.value?.map((item) => {
      if (item[props.valueField] == attrs.modelValue) needClear = false
    })

    // 清空初始modelValue值
    if (needClear) handleChange()
  }

  isFirstLoad.value = false
}

// 下拉框出现时，加载数据
async function handleFetch(visible) {
  if (visible) {
    if (props.alwaysLoad) {
      await fetch()
    } else if (!selectOptions.value.length && isFirstLoad.value) {
      await fetch()
    }
  }
}

// 数据变化时，触发回调
function handleChange(val = null) {
  let option = {}
  for (const key in selectOptions.value) {
    if (Object.hasOwnProperty.call(selectOptions.value, key)) {
      const element = selectOptions.value[key]
      if (element[props.valueField] == val) {
        option = element
        break
      }
    }
  }

  emit('update:modelValue', val)
  emit('change', val, option)
}
</script>
